package com.wn.pay.service.impl;
import com.google.common.collect.Lists;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wn.access.api.BankAccessApi;
import com.wn.common.model.dto.trade.bid.BankBidDTO;
import com.wn.common.model.dto.trade.payback.AgencyBidPayDTO;
import com.wn.common.model.dto.trade.payback.BorrowRecordPayDTO;
import com.wn.common.model.dto.trade.payback.PayBackDTO;
import com.wn.common.model.vo.pay.RepayItemVo;
import com.wn.common.model.vo.pay.RepayVo;
import com.wn.common.result.Result;
import com.wn.common.utils.GeneratorCodeUtil;
import com.wn.pay.entity.AgencyReceiveItem;
import com.wn.pay.entity.AgencyReceivePlan;
import com.wn.pay.entity.BorrowerPayItem;
import com.wn.pay.entity.BorrowerPayPlan;
import com.wn.pay.mapper.AgencyReceiveItemMapper;
import com.wn.pay.mapper.AgencyReceivePlanMapper;
import com.wn.pay.mapper.BorrowerPayItemMapper;
import com.wn.pay.mapper.BorrowerPayPlanMapper;
import com.wn.pay.service.IBorrowerPayPlanService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wn.pay.utils.DEBXRepayVo;
import com.wn.pay.utils.DateUtil;
import com.wn.pay.utils.RepayCommon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 借款人还款计划 服务实现类
 * </p>
 *
 * @author yellow docker
 * @since 2023-03-08
 */
@Service
@Slf4j
public class BorrowerPayPlanServiceImpl extends ServiceImpl<BorrowerPayPlanMapper, BorrowerPayPlan> implements IBorrowerPayPlanService {

    @Autowired
    private AgencyReceivePlanMapper agencyReceivePlanMapper;

    @Autowired
    private BorrowerPayPlanMapper borrowerPayPlanMapper;

    @Autowired
    private BorrowerPayItemMapper borrowerPayItemMapper;

    @Autowired
    private AgencyReceiveItemMapper agencyReceiveItemMapper;

    @Autowired
    private BankAccessApi bankAccessApi;

    /**
     * 生成还款计划
     * @param payBackDTO
     * @return
     */
    @Override
    public String payPlan(PayBackDTO payBackDTO) {
        //（1）、获取借款项目信息
        //（2）、获取投标信息
        //（3）、计算还款的月数、生成还款计划、保存还款计划
        //（1）、根据机构申购记录生成应收明细
        //（2）、保存应收明细到数据库
        //1.生成借款人还款计划
        //1.1 获取借款项目信息
        BorrowRecordPayDTO borrowRecordPayDTO = payBackDTO.getBorrowRecordPayDTO();
        //1.2 获取投标信息
        List<AgencyBidPayDTO> agencyBids = payBackDTO.getAgencyBids();
        //1.3 计算还款的月数
        Double ceil = Math.ceil(borrowRecordPayDTO.getPeriod()/30.0);
        Integer month = ceil.intValue();
        //1.4 还款方式，只针对等额本息
        String repaymentWay = borrowRecordPayDTO.getRepaymentWay();

        //1.5 调用工具类，生成还款计划
        //拿到 DEBXRepayVo （ 等额本息还款 ） (每个月的利息是多少 、还款金额是多少 保存到db中  )
        DEBXRepayVo fixedRepayment =
                RepayCommon.fixedRepayment(
                        borrowRecordPayDTO.getAmount(),
                        borrowRecordPayDTO.getBorrowerRate(),
                        month,borrowRecordPayDTO.getCommissionRate()
                );

        //1.6 保存还款计划

        //还款对应的表  sddfp_pay_back 库下面的   borrower_pay_plan 还款计划表
        List<BorrowerPayPlan> planList = savePayPlan(borrowRecordPayDTO,fixedRepayment);

        //2.生成投资人应收明细
        //2.1 根据投标信息生成应收明细
        //一个借款记录有多个标  ( sddfp_pay_back 库下面  agency_receive_plan 表 会生成 收款人的明细  )
        agencyBids.forEach(agencyBidDTO -> {
            DEBXRepayVo receipt = RepayCommon.fixedRepayment(
                    agencyBidDTO.getAmount(),
                    agencyBidDTO.getInvestRate(),
                    month,
                    payBackDTO.getInvestorAnnualRate());

            //2.2 保存应收明细到数据库
            planList.forEach(plan -> {
                saveAgencyReceivePlan(plan,agencyBidDTO,receipt);
            });

        });

        return planList.toString();
    }

    //还款的操作
    @Override
    public void doRepay(String date) {

        //01 拿到 还款计划
        List<BorrowerPayPlan> payPlans = this.baseMapper.findPayPlanByDate(date);

        //生成一个还款的明细
        payPlans.forEach(payPlan -> {

            //你的还款操作可以进行记录
            //把  借款记录  BorrowerPayPlan 中数据就进行存储 到   borrower_pay_item 表中  
            BorrowerPayItem borrowerPayItem =saveBorrowerPayItem(payPlan);
            //给银行系统发送数据进行 冻结 金额

            BankBidDTO bankBidDTO = new BankBidDTO();

            bankBidDTO.setAmount(borrowerPayItem.getAmount());
            bankBidDTO.setReqCode(GeneratorCodeUtil.generator("BORR"));
            bankBidDTO.setUserCode(payPlan.getUserCode());
            bankBidDTO.setBizType("REPAYMENT");
            bankBidDTO.setBorrowRecordNo(payPlan.getBorrowRecordNo());
            //给还款计划id
            bankBidDTO.setId(payPlan.getId());
            Result<String> bankResult = bankAccessApi.bankBidService(bankBidDTO);
            if (bankResult.getCode() == 200 ){
                log.info("冻结成功....");
                //创建一个还款的vo
                RepayVo repayVo = createRepayVo(payPlan,borrowerPayItem);
                //修改状态
                this.modifyStatus(payPlan,repayVo);

                // 发送数据到银行 系统
                Result<String> stringResult = bankAccessApi.confirmRepay(repayVo);
                if (stringResult.getCode() == 200) {
                    log.info("还款成功...");
                }else{
                    log.info("失败成功...");
                }
            }
        });

    }

    @Transactional
    public Boolean modifyStatus(BorrowerPayPlan borrowerPayPlan, RepayVo repayVo) {
        //1. 更新还款明细：已同步
        String preRequestNo = repayVo.getPreReqCode();

        //BorrowerPayItem  这个库下面 的 sddfp_pay_back  表  borrower_pay_item 中的数据

        borrowerPayItemMapper.update(null,
                Wrappers.lambdaUpdate(BorrowerPayItem.class)
                        .set(BorrowerPayItem::getStatus,1)
                        .eq(BorrowerPayItem::getReqCode,preRequestNo));

        //2.1 更新receive_plan应收计划表为：已收
        //根据还款计划id，查询应收计划
        List<AgencyReceivePlan> agencyReceivePlanList =
                agencyReceivePlanMapper.selectList(
                        Wrappers.lambdaQuery(AgencyReceivePlan.class)
                                .eq(AgencyReceivePlan::getPayPlanId,borrowerPayPlan.getId()));

        agencyReceivePlanList.forEach(receivePlan -> {
            // 投资人 的 收款计划   修改    agency_receive_plan 表中的数据   (receiveStatus 字段  )
            receivePlan.setReceiveStatus(1);
            agencyReceivePlanMapper.updateById(receivePlan);

            //2.2 保存数据到receivable_detail 应收明细
            // 构造应收明细
            AgencyReceiveItem agencyReceiveItem = new AgencyReceiveItem();
            // 应收项标识
            agencyReceiveItem.setReceiveId(receivePlan.getId());
            // 实收本息
            agencyReceiveItem.setAmount(receivePlan.getAmount());
            // 实收时间
            agencyReceiveItem.setReceiveDate(LocalDateTime.now());
            // 保存投资人应收明细
            agencyReceiveItemMapper.insert(agencyReceiveItem);
        });

        //3. 更新还款计划：已还款
        borrowerPayPlan.setPayStatus("1");
        int rows = baseMapper.updateById(borrowerPayPlan);
        return rows>0;
    }


    private RepayVo createRepayVo(BorrowerPayPlan payPlan,BorrowerPayItem borrowerPayItem) {

        //根据还款计划id,查询应收计划
        //一个 还款的商品 可能 有多个 人购买 (这里是集合 List )
        List<AgencyReceivePlan> receivablePlanList =
                agencyReceivePlanMapper.selectList(
                        Wrappers.lambdaQuery(AgencyReceivePlan.class)
                                .eq(AgencyReceivePlan::getPayPlanId,payPlan.getId()));

        RepayVo repayVo = new RepayVo();
        repayVo.setReqCode(borrowerPayItem.getReqCode());
        repayVo.setPreReqCode(GeneratorCodeUtil.generator("REPAY"));
        repayVo.setBorrowRecordNo(payPlan.getBorrowRecordNo());
        repayVo.setCommission(payPlan.getCommission());
        repayVo.setId(payPlan.getId());
        repayVo.setAmount(payPlan.getAmount());

        List<RepayItemVo> detailRequests =new ArrayList<>();
        receivablePlanList.forEach(receivablePlan -> {
            RepayItemVo detailRequest=new RepayItemVo();
            // 投资人用户编码
            detailRequest.setUserCode(receivablePlan.getUserCode());
            // 向投资人收取的佣金
            detailRequest.setCommission(receivablePlan.getCommission());
            // 投资人应得本金
            detailRequest.setAmount(receivablePlan.getPrincipal());
            // 投资人应得利息
            detailRequest.setInterest(receivablePlan.getInterest());
            //添加到集合
            detailRequests.add(detailRequest);
        });

        repayVo.setDetails(detailRequests);
        return repayVo;

    }

    private BorrowerPayItem saveBorrowerPayItem(BorrowerPayPlan borrowerPayPlan) {
        //1. 进行查询 (根据还款计划查询还款明细，不存在保存)
        BorrowerPayItem payItem= borrowerPayItemMapper.selectOne(
                Wrappers.lambdaQuery(BorrowerPayItem.class)
                        .eq(BorrowerPayItem::getPayPlanId,borrowerPayPlan.getId()));
        //2. 查不到数据才进行保存
        if(payItem == null){
            payItem = new BorrowerPayItem();
            // 还款计划项标识
            payItem.setPayPlanId(borrowerPayPlan.getId());
            // 实还本息
            payItem.setAmount(borrowerPayPlan.getAmount());
            // 实际还款时间
            payItem.setPayDate(LocalDateTime.now());
            // 请求流水号
            payItem.setReqCode(GeneratorCodeUtil.generator("PAY"));
            // 未同步
            payItem.setStatus(0);
            // 保存数据
            borrowerPayItemMapper.insert(payItem);
        }
        return payItem;
    }

    /**
     * 保存应收明细到数据库
     * @param repaymentPlan
     * @param agencyBidPayDTO
     * @param receipt
     */
    private void saveAgencyReceivePlan(BorrowerPayPlan repaymentPlan,
                                       AgencyBidPayDTO agencyBidPayDTO,
                                       DEBXRepayVo receipt) {
        // 应收本金
        Map<Integer, BigDecimal> principalMap = receipt.getPrincipalMap();
        // 应收利息
        Map<Integer, BigDecimal> interestMap = receipt.getInterestMap();
        // 平台收取利息
        Map<Integer, BigDecimal> commissionMap = receipt.getCommissionMap();
        // 封装投资人应收明细
        AgencyReceivePlan receivablePlan = new AgencyReceivePlan();
        // 投标信息标识
        receivablePlan.setAgencyBidId(agencyBidPayDTO.getId());
        // 设置期数
        receivablePlan.setNumberOfPeriods(repaymentPlan.getNumberOfPeriods());
        // 投标人用户标识
        receivablePlan.setUserId(agencyBidPayDTO.getUserId());
        // 投标人用户编码
        receivablePlan.setUserCode(agencyBidPayDTO.getUserCode());
        // 还款计划项标识
        receivablePlan.setPayPlanId(repaymentPlan.getId());
        // 应收利息
        receivablePlan.setInterest(interestMap.get(repaymentPlan
                .getNumberOfPeriods()));
        // 应收本金
        receivablePlan.setPrincipal(principalMap.get(repaymentPlan
                .getNumberOfPeriods()));
        // 应收本息 = 应收本金 + 应收利息
        receivablePlan.setAmount(receivablePlan.getInterest()
                .add(receivablePlan.getPrincipal()));
        // 应收时间
        receivablePlan.setShouldReceiveDate(repaymentPlan.getShouldPayDate());
        // 应收状态, 当前业务为未收
        receivablePlan.setReceiveStatus(0);
        // 创建时间
        receivablePlan.setCreateDate(DateUtil.now());
        // 设置投资人让利, 注意这个地方是具体佣金
        receivablePlan.setCommission(commissionMap
                .get(repaymentPlan.getNumberOfPeriods()));
        // 保存到数据库
        agencyReceivePlanMapper.insert(receivablePlan);
    }

    /**
     *  保存还款计划到数据库
     */
    private List<BorrowerPayPlan> savePayPlan(
            BorrowRecordPayDTO borrowRecordPayDTO,
            DEBXRepayVo fixedRepayment) {

        List<BorrowerPayPlan> payPlanList = new ArrayList<>();

        // 获取每期利息
        Map<Integer, BigDecimal> interestMap = fixedRepayment.getInterestMap();

        // 平台收取利息
        Map<Integer, BigDecimal> commissionMap = fixedRepayment.getCommissionMap();

        // 获取每期本金
        fixedRepayment.getPrincipalMap().forEach((k, v) -> {
            // 封装还款计划对象
            BorrowerPayPlan repaymentPlan = new BorrowerPayPlan();
            // 设置借款记录ID、借款人ID、借款人用户编码、借款编号、期数
            repaymentPlan.setBorrowRecordId(borrowRecordPayDTO.getId());
            repaymentPlan.setUserId(borrowRecordPayDTO.getUserId());
            repaymentPlan.setUserCode(borrowRecordPayDTO.getUserCode());
            repaymentPlan.setBorrowRecordNo(borrowRecordPayDTO.getBorrowRecordNo());
            repaymentPlan.setNumberOfPeriods(k);
            // 当期还款利息
            repaymentPlan.setInterest(interestMap.get(k));
            // 还款本金
            repaymentPlan.setPrincipal(v);
            // 本息 = 本金 + 利息
            repaymentPlan.setAmount(repaymentPlan.getPrincipal()
                    .add(repaymentPlan.getInterest()));
            // 应还时间 = 当前时间 + 期数( 单位月 )
            repaymentPlan.setShouldPayDate(DateUtil.localDateTimeAddMonth(DateUtil.now(), k));
            // 应还状态, 当前业务为待还
            repaymentPlan.setPayStatus("0");
            // 计划创建时间
            repaymentPlan.setCreateDate(DateUtil.now());
            // 设置平台佣金( 借款人让利 ) 注意这个地方是 具体佣金
            repaymentPlan.setCommission(commissionMap.get(k));
            // 保存到数据库 (borrowPayPlanMapper)
            baseMapper.insert(repaymentPlan);
            payPlanList.add(repaymentPlan);
        });
        return payPlanList;
    }

}
